import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import 'package:flutter_k_chart/chart_theme.dart';

import 'chart_style.dart';
import 'entity/info_window_entity.dart';
import 'entity/k_line_entity.dart';
import 'generated/l10n.dart';
import 'renderer/chart_painter.dart';
import 'utils/date_format_util.dart';
import 'utils/number_util.dart';

enum MainState { MA, BOLL, NONE }
enum VolState { VOL, NONE }
enum SecondaryState { MACD, KDJ, RSI, WR, NONE }

class KChartWidget extends StatefulWidget {
  final List<KLineEntity> datas;
  final MainState mainState;
  final VolState volState;
  final SecondaryState secondaryState;
  final bool isLine;
  final ChartStyle? chartStyle;
  final ChartColors? chartColors;
  final VoidCallback? onTapDown;
  final bool isCustomChart;
  final AnimationController scrollXController;
  KChartWidget(
    this.datas, {
    Key? key,
    this.chartStyle,
    this.chartColors,
    this.mainState = MainState.MA,
    this.volState = VolState.VOL,
    this.secondaryState = SecondaryState.MACD,
    this.isLine = false,
    int fractionDigits = 2,
    this.onTapDown,
    this.isCustomChart = false,
        required this.scrollXController,
  })  : assert(fractionDigits >= 0),
        super(key: key) {
    NumberUtil.fractionDigits = fractionDigits;
  }

  @override
  _KChartWidgetState createState() => _KChartWidgetState();
}

const _kScaleDefault = 1.0;

///最小缩放倍数
const _kScaleMin = 0.2;

///最大缩放倍数
const _kScaleMax = 2.0;

class _KChartWidgetState extends State<KChartWidget>
    with TickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  double mScaleX = _kScaleDefault, mScrollX = 0.0, mSelectX = 0.0;
  double? mSelectY;

  StreamController<InfoWindowEntity?>? mInfoWindowStream;
  double mWidth = 0;
  // late AnimationController _scrollXController;

  double? getMinScrollX() => mScaleX;

  double _lastScale = _kScaleDefault;
  bool isScale = false, isDrag = false, isShowInfo = false;

  late ChartColors chartColors;
  late ChartStyle chartStyle;
  late ValueNotifier<Rect?> onRealPriceRectNotifier;

  GlobalKey _infoKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    onRealPriceRectNotifier = ValueNotifier<Rect?>(null);
    mInfoWindowStream = StreamController<InfoWindowEntity?>();
    _controller = AnimationController(
        duration: const Duration(milliseconds: 850), vsync: this);
    _animation = Tween(begin: 0.9, end: 0.1).animate(_controller)
      ..addListener(() => setState(() {}));
    // _scrollXController = AnimationController(
    //     vsync: this,
    //     duration: const Duration(milliseconds: 500),
    //     lowerBound: double.negativeInfinity,
    //     upperBound: double.infinity);
    _scrollListener();
  }

  void _scrollListener() {
    widget.scrollXController.addListener(() {
      mScrollX = widget.scrollXController.value;
      // debugPrint('_KChartWidgetState._scrollListener mScrollX=$mScrollX');
      if (mScrollX <= 0) {
        mScrollX = 0;
        _stopAnimation();
      } else if (mScrollX >= ChartPainter.maxScrollX) {
        mScrollX = ChartPainter.maxScrollX;
        _stopAnimation();
      } else {
        notifyChanged();
      }
    });
    widget.scrollXController.addStatusListener((status) {
      if (status == AnimationStatus.completed ||
          status == AnimationStatus.dismissed) {
        isDrag = false;
        notifyChanged();
      }
    });
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    mWidth = MediaQuery.of(context).size.width;
  }

  @override
  void didUpdateWidget(KChartWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.datas != widget.datas) {
      mScrollX = mSelectX = 0.0;
      mScaleX = _kScaleDefault;
      hideInfo();
    }
  }

  @override
  void dispose() {
    mInfoWindowStream?.close();
    _controller.dispose();
    widget.scrollXController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {


    if ( widget.datas.isEmpty) {
      mScrollX = mSelectX = 0.0;
      mSelectY = null;
      mScaleX = 1.0;
    }
    final dark = Theme.of(context).brightness == Brightness.dark;
    chartColors = (dark ? ChartColors.dark() : ChartColors.light())
        .merge(widget.chartColors);
    chartStyle = const ChartStyle().merge(widget.chartStyle);
    return ChartTheme(
      chartStyle: chartStyle,
      chartColors: chartColors,
      child: GestureDetector(
        onHorizontalDragDown: (details) {
          if (isScale) return;
          _stopAnimation();
          isDrag = true;
        },
        onHorizontalDragUpdate: (details) {
          if (isScale) return;
          hideInfo();
          if (isScale || isShowInfo) return;
          mScrollX = ((details.primaryDelta ?? 0) / mScaleX + mScrollX)
              .clamp(0.0, ChartPainter.maxScrollX)
              .toDouble();
          notifyChanged();
        },
        onHorizontalDragEnd: (DragEndDetails details) {
          // debugPrint(
          //     '_KChartWidgetState.build onHorizontalDragEnd mScrollX=$mScrollX ,${widget.scrollXController.value}');
          // isDrag = false;
          final Tolerance tolerance = Tolerance(
            velocity: 1.0 /
                (0.050 * WidgetsBinding.instance!.window.devicePixelRatio),
            // logical pixels per second
            distance: 1.0 /
                WidgetsBinding
                    .instance!.window.devicePixelRatio, // logical pixels
          );
          ClampingScrollSimulation simulation = ClampingScrollSimulation(
            position: mScrollX,
            velocity: details.primaryVelocity ?? 0.0,
            tolerance: tolerance,
          );
          widget.scrollXController.animateWith(simulation);
        },
        onHorizontalDragCancel: () {
          isDrag = false;
        },
        onScaleStart: (_) {
          hideInfo();
          isScale = true;
        },
        onScaleUpdate: (details) {
          if (isDrag || isShowInfo) return;
          mScaleX = (_lastScale * details.scale).clamp(_kScaleMin, _kScaleMax);
          notifyChanged();
        },
        onScaleEnd: (_) {
          isScale = false;
          _lastScale = mScaleX;
        },
        onTapDown: (details) {
          widget.onTapDown?.call();

          if (null != onRealPriceRectNotifier.value) {
            var rect = onRealPriceRectNotifier.value;
            // RenderBox box = _infoKey.currentContext.findRenderObject();
            // var offset = box.globalToLocal(Offset.zero);
            // var size = box.size;
            //
            // var infoRect =
            //     Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height);
            //实时价格被Info挡住也能点击
            if (/*!infoRect.contains(details.localPosition) &&*/
                rect?.contains(details.localPosition) ?? false) {
              hideInfo();
              //点击图中的实时价格回到最右边当前价格位置
              final Tolerance tolerance = Tolerance(
                velocity: 1.0 /
                    (0.050 * WidgetsBinding.instance!.window.devicePixelRatio),
                // logical pixels per second
                distance: 1.0 /
                    WidgetsBinding
                        .instance!.window.devicePixelRatio, // logical pixels
              );
              ClampingScrollSimulation simulation = ClampingScrollSimulation(
                position: 0,
                velocity: -1000,
                tolerance: tolerance,
              );
              widget.scrollXController.animateWith(simulation);
              return;
            }
          }

          if (details.localPosition.dy < (chartStyle.topPadding ?? 0)) {
            hideInfo();
            return;
          }
          isShowInfo = true;
          if (mSelectX != details.localPosition.dx ||
              mSelectY != details.localPosition.dy) {
            mSelectX = details.localPosition.dx;
            mSelectY = details.localPosition.dy;
            notifyChanged();
          }
        },
        onLongPressStart: (details) {
          isShowInfo = true;
          mSelectY = null;
          if (mSelectX != details.localPosition.dx) {
            mSelectX = details.localPosition.dx;
            notifyChanged();
          }
        },
        onLongPressMoveUpdate: (details) {
          mSelectY = null;
          if (mSelectX != details.localPosition.dx) {
            mSelectX = details.localPosition.dx;
            notifyChanged();
          }
        },
        onLongPressEnd: (details) {
          mSelectY = null;
          // hideInfo();
        },
        child: Stack(
          children: <Widget>[
            RepaintBoundary(
              child: CustomPaint(
                size: Size(double.infinity, double.infinity),
                painter: ChartPainter(
                  onRealtimeRectNotifier: onRealPriceRectNotifier,
                  datas: widget.datas,
                  scaleX: mScaleX,
                  scrollX: mScrollX,
                  selectX: mSelectX,
                  selectY: mSelectY,
                  isLongPass: isShowInfo,
                  mainState: widget.mainState,
                  volState: widget.volState,
                  secondaryState: widget.secondaryState,
                  isLine: widget.isLine,
                  sink: mInfoWindowStream?.sink,
                  opacity: _animation.value,
                  controller: _controller,
                  chartStyle: chartStyle,
                  chartColors: chartColors,
                  isCustomChart: widget.isCustomChart

                ),
              ),
            ),
            _buildInfoDialog()
          ],
        ),
      ),
    );
  }

  void hideInfo() {
    if (true == isShowInfo) {
      isShowInfo = false;
      mInfoWindowStream?.sink.add(null);
      notifyChanged();
    }
  }

  void _stopAnimation() {
    if (widget.scrollXController.isAnimating) {
      widget.scrollXController.stop();
      isDrag = false;
      notifyChanged();
    }
  }

  void notifyChanged() => setState(() {});

  List<String> infoNames = ["时间", "开", "高", "低", "收", "涨跌额", "涨幅跌", "成交量"];

  // List<String> infoNames = [
  //   "Date",
  //   "Open",
  //   "H",
  //   "L",
  //   "Close",
  //   "Change",
  //   "Change%",
  //   //todo: 暂时不显示交易量
  //   // "Executed"
  // ];
  List? infos;

  Widget _buildInfoDialog() {

    return StreamBuilder<InfoWindowEntity?>(
        stream: mInfoWindowStream?.stream,
        builder: (context, snapshot) {
          if (!isShowInfo ||
              widget.isLine == true ||
              !snapshot.hasData ||
              snapshot.data?.kLineEntity == null) {
            return const SizedBox();
          }

          KLineEntity entity = snapshot.data!.kLineEntity;
          double change =
              (entity.change ?? (entity.close - entity.open)).toDouble();
          double changePercent =
              100.0 * (entity.changePercent ?? change / entity.open);
          infos = [
            getDate(entity.id, snapshot.data?.datePattern),
            NumberUtil.format(entity.open),
            NumberUtil.format(entity.high),
            NumberUtil.format(entity.low),
            NumberUtil.format(entity.close),
            "${change > 0 ? "+" : ""}${NumberUtil.format(change)}",
            "${changePercent > 0 ? "+" : ''}${changePercent.toStringAsFixed(2)}%",
            //todo: 暂时不显示交易量
            NumberUtil.volFormat(entity.vol)
          ];
          return Align(
            alignment: (snapshot.data?.isLeft ?? false)
                ? Alignment.topLeft
                : Alignment.topRight,
            child: Container(
              margin: EdgeInsets.only(left: 8, right: 53, top: 8),
              child: StatefulBuilder(
                  key: _infoKey,
                  builder: (context, _) {
                    return Card(
                      elevation: 4,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                      ),
                      shadowColor: Colors.black38,
                      color: chartColors.markerBgColor,
                      child: Container(
                        padding:
                            EdgeInsets.symmetric(horizontal: 8, vertical: 8),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisSize: MainAxisSize.min,
                          children: List.generate(
                              infoNames.length,
                              (i) => _buildItem(
                                  infos![i].toString(), infoNames[i])),
                        ),
                      ),
                    );
                  }),
            ),
          );
        });
  }

  Widget _buildItem(String info, String infoName) {
    Color? color = chartColors.markerInfoColor;
    if (info.startsWith("+")) {
      color = chartColors.upColor;
    } else if (info.startsWith("-")) {
      color = chartColors.dnColor;
    }
    return Container(
      constraints: BoxConstraints(
          minWidth: 95, maxWidth: 110, maxHeight: 14.0, minHeight: 14.0),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Text("$infoName",
              style: TextStyle(
                  color: chartColors.markerInfoNameColor,
                  fontSize: chartStyle.defaultTextSize)),
          SizedBox(width: 5),
          Text(info,
              style: TextStyle(
                  color: color, fontSize: chartStyle.defaultTextSize)),
        ],
      ),
    );
  }

  String getDate(int date, String? pattern) {
    return DateFormatUtil.dateFormat(
        DateTime.fromMillisecondsSinceEpoch(date * 1000), pattern);
  }
}
